/**
 * 
 */
package com.atjava.nebula.core.dao.ibatis;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.atjava.nebula.core.dao.BaseDao;
import com.atjava.nebula.core.dao.dialect.LimitSqlExecutor;
import com.atjava.nebula.core.dao.ibatis.projection.IProjection;
import com.atjava.nebula.core.dao.ibatis.script.MsSQL2KScript;
import com.atjava.nebula.core.dao.ibatis.script.SqlScript;
import com.atjava.nebula.core.exception.baseExp.DataAccessException;
import com.atjava.nebula.core.model.BaseModel;
import com.atjava.nebula.core.page.Page;
import com.atjava.nebula.core.util.reflection.ReflectUtil;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.engine.execution.SqlExecutor;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;

/**
 * 基础Dao的IBatis实现
 * 
 * @author kyo
 * 
 */

public class BaseIBatisDao extends SqlMapClientDaoSupport implements BaseDao {

	private SqlExecutor sqlExecutor;

	public SqlExecutor getSqlExecutor() {
		return sqlExecutor;
	}

	public void setSqlExecutor(SqlExecutor sqlExecutor) {
		this.sqlExecutor = sqlExecutor;
	}

	public void setEnableLimit(boolean enableLimit) {
		if (sqlExecutor instanceof LimitSqlExecutor) {
			((LimitSqlExecutor) sqlExecutor).setEnableLimit(enableLimit);
		}
	}

	@SuppressWarnings("deprecation")
	public void initialize() {
		if (sqlExecutor != null) {
			SqlMapClient sqlMapClient = getSqlMapClientTemplate()
					.getSqlMapClient();
			if (sqlMapClient instanceof ExtendedSqlMapClient) {
				ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient)
						.getDelegate(), "sqlExecutor", SqlExecutor.class,
						sqlExecutor);
			}
		}
	}

	public <T> String getDomain(T obj) {
		return obj.getClass().getSimpleName();
	}

	public <T> String getDomain(Class<T> entityClass) {
		return entityClass.getSimpleName();
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#getAll(java.lang.Class)
	 */
	public <T extends BaseModel> List<T> getAll(Class<T> entityClass)
			throws DataAccessException {
		return list(entityClass, null);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#getAll(java.lang.String,
	 *      java.lang.Class)
	 */
	public <T extends BaseModel> List<T> getAll(String ibatisMapId,
			Class<T> entityClass) throws DataAccessException {
		return list(ibatisMapId, entityClass, null);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#create(com.atjava.nebula.core.model.BaseModel)
	 */
	public <T extends BaseModel, ID extends Serializable> ID create(T obj)
			throws DataAccessException {
		String mapping = this.getDomain(obj) + IBATIS_CREATE;
		return this.create(mapping, obj);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#create(java.lang.String,
	 *      com.atjava.nebula.core.model.BaseModel)
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseModel, ID extends Serializable> ID create(
			String ibatisMapId, T obj) throws DataAccessException {
		// T domain=null;
		ID id;
		try {
			id = (ID) this.getSqlMapClientTemplate().insert(ibatisMapId, obj);
			
		} catch (UncategorizedSQLException e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_CREATE
					+ "（数据超过数据库字段的长度）");
		} catch (Exception e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_CREATE);
		}
		return id;
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#updateById(com.atjava.nebula.core.model.BaseModel)
	 */
	public <T extends BaseModel> void updateById(T obj)
			throws DataAccessException {
		String mapping = this.getDomain(obj) + IBATIS_UPDATE;
		this.updateById(mapping, obj);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#updateById(java.lang.String,
	 *      com.atjava.nebula.core.model.BaseModel)
	 */
	public <T extends BaseModel> void updateById(String ibatisMapId, T obj)
			throws DataAccessException {
		try {
			this.getSqlMapClientTemplate().update(ibatisMapId, obj);
		} catch (UncategorizedSQLException e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_UPDATE
					+ "（数据超过数据库字段的长度）");
		} catch (Exception e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_UPDATE);
		}
	}

	/* 
	 * @see com.atjava.nebula.core.dao.BaseDao#updateAllById(com.atjava.nebula.core.model.BaseModel)
	 */
	public <T extends BaseModel> void updateAllById(T obj) throws DataAccessException {
		String mapping = this.getDomain(obj) + IBATIS_UPDATE_ALL;
		this.updateById(mapping, obj);
	}
	
	/**根据指定的表主键（ID）更新对象，该方法将更新所有字段，包含值为空(null)的属性
	 * @param <T>
	 * @param ibatisMapId 
	 * @param obj
	 * @throws DataAccessException
	 */
	public <T extends BaseModel> void updateAllById(String ibatisMapId,T obj) throws DataAccessException {
		try {
			this.getSqlMapClientTemplate().update(ibatisMapId, obj);
		} catch (UncategorizedSQLException e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_UPDATE
					+ "（数据超过数据库字段的长度）");
		} catch (Exception e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_UPDATE);
		}		
	}	
	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#remove(com.atjava.nebula.core.model.BaseModel)
	 */
	public <T extends BaseModel> void remove(T obj) throws DataAccessException {
		String mapping = this.getDomain(obj) + IBATIS_DELETE;
		this.remove(mapping, obj);
	}

	public <T extends BaseModel> void remove(String ibatisMapId, T obj)
			throws DataAccessException {
		Condition cond = new Condition(obj);
		try {
			this.getSqlMapClientTemplate().delete(ibatisMapId, cond);
		} catch (Exception e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_DELETE);
		}
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#removeById(java.lang.Class,
	 *      java.io.Serializable)
	 */
	public <T extends BaseModel, ID extends Serializable> void removeById(
			Class<T> entityClass, ID idObj) throws DataAccessException {
		String mapping = this.getDomain(entityClass) + IBATIS_DELETEBYID;
		this.removeById(mapping, idObj);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#removeById(java.lang.String,
	 *      java.io.Serializable)
	 */
	public <T extends BaseModel, ID extends Serializable> void removeById(
			String ibatisMapId, ID idObj) throws DataAccessException {

		try {
			this.getSqlMapClientTemplate().delete(ibatisMapId, idObj);
		} catch (Exception e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_DELETE);
		}
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#getById(java.lang.String,
	 *      java.io.Serializable)
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseModel, ID extends Serializable> T getById(
			String ibatisMapId, ID id) throws DataAccessException {

		T domain = null;

		try {
			domain = (T) this.getSqlMapClientTemplate().queryForObject(
					ibatisMapId, id);
		} catch (Exception e) {
			throw new DataAccessException(e, EXCEPTION_GET);
		}
		return domain;
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#getById(java.lang.Class,
	 *      java.io.Serializable)
	 */
	public <T extends BaseModel, ID extends Serializable> T getById(
			Class<T> entityClass, ID id) throws DataAccessException {

		String mapping = this.getDomain(entityClass) + IBATIS_GETBYID;
		return getById(mapping, id);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#list(com.atjava.nebula.core.model.BaseModel)
	 */
	public <T extends BaseModel> List<T> list(T obj) throws DataAccessException {
		String mapping = this.getDomain(obj) + IBATIS_LIST;
		return this.list(mapping, obj);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#list(java.lang.String,
	 *      com.atjava.nebula.core.model.BaseModel)
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseModel> List<T> list(String ibatisMapId, T obj)
			throws DataAccessException {
		Condition cond = new Condition(obj);
		return (List<T>) list(ibatisMapId, obj.getClass(), cond);

	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#getByList(java.lang.Class,
	 *      com.atjava.nebula.core.dao.ibatis.Condition)
	 */
	public <T extends BaseModel> List<T> list(Class<T> entityClass,
			Condition cond) throws DataAccessException {
		String mapping = this.getDomain(entityClass) + IBATIS_GETBYCOND;
		return this.list(mapping, entityClass, cond);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#list(java.lang.String,
	 *      java.lang.Class, com.atjava.nebula.core.dao.ibatis.Condition)
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseModel> List<T> list(String ibatisMapId,
			Class<T> entityClass, Condition cond) throws DataAccessException {

		List<T> list = null;
		try {
			list = this.getSqlMapClientTemplate().queryForList(ibatisMapId,
					cond);
		} catch (Exception e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_LIST);
		}
		return list;
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#list(java.lang.Class,
	 *      com.atjava.nebula.core.dao.ibatis.projection1.IProjection,
	 *      com.atjava.nebula.core.dao.ibatis.Condition)
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseModel> List<Map<String, Object>> list(
			Class<T> entityClass, IProjection projection, Condition cond)
			throws DataAccessException {
		List<Map<String, Object>> list = null;
		String mapId = this.getDomain(entityClass) + IBATIS_GETBYPROJ;
		ProjAndCond projCond = new ProjAndCond(projection, cond);
		try {
			list = this.getSqlMapClientTemplate().queryForList(mapId, projCond);
		} catch (Exception e) {
			throw new DataAccessException(e, mapId + EXCEPTION_GROUPBYSQL);
		}
		return list;
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#list(com.atjava.nebula.core.model.BaseModel,
	 *      com.atjava.nebula.core.dao.ibatis.projection1.IProjection)
	 */
	public <T extends BaseModel> List<Map<String, Object>> list(T obj,
			IProjection projection) throws DataAccessException {
		Condition cond = new Condition(obj);
		return list(obj.getClass(), projection, cond);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#page(com.atjava.nebula.core.model.BaseModel,
	 *      int, int)
	 */
	public <T extends BaseModel> Page<T> page(T obj, int pageNo, int pageSize)
			throws DataAccessException {
		String mapping = this.getDomain(obj) + IBATIS_GETBYCOND;
		String countting = this.getDomain(obj) + IBATIS_COUNT;
		return this.page(mapping, countting, obj, pageNo, pageSize);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#page(java.lang.String,
	 *      java.lang.String, com.atjava.nebula.core.model.BaseModel, int, int)
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseModel> Page<T> page(String ibatisMapId,
			String countMapId, T obj, int pageNo, int pageSize)
			throws DataAccessException {
		Condition cond = new Condition(obj);
		return (Page<T>) this.page(ibatisMapId, countMapId, obj.getClass(),
				cond, pageNo, pageSize);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#page(java.lang.Class,
	 *      com.atjava.nebula.core.dao.ibatis.Condition, int, int)
	 */
	public <T extends BaseModel> Page<T> page(Class<T> entityClass,
			Condition cond, int pageNo, int pageSize)
			throws DataAccessException {
		String mapping = this.getDomain(entityClass) + IBATIS_GETBYCOND;
		String countting = this.getDomain(entityClass) + IBATIS_COUNT;
		return this.page(mapping, countting, entityClass, cond, pageNo,
				pageSize);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#page(java.lang.String,
	 *      java.lang.String, java.lang.Class,
	 *      com.atjava.nebula.core.dao.ibatis.Condition, int, int)
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseModel> Page<T> page(String ibatisMapId,
			String countMapId, Class<T> entityClass, Condition cond,
			int pageNo, int pageSize) throws DataAccessException {
		Page<T> page = null;
		List<T> list = null;
		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		Integer totalCount = count(entityClass, cond);

		if ((totalCount == null) || (totalCount == 0)) {
			// throw new DataAccessException(ibatisMapId + EXCEPTION_COUNT);
			list = new ArrayList<T>();
		} else {

			try {
				// getSqlMapClientTemplate().getSqlMapClient().
				if (pageSize > 0 && pageNo > 0) {
					list = getSqlMapClientTemplate().queryForList(ibatisMapId,
							cond, (pageNo - 1) * pageSize, pageSize);
				} else {
					list = getSqlMapClientTemplate().queryForList(ibatisMapId,
							cond);
				}
			} catch (Exception e) {
				throw new DataAccessException(e, ibatisMapId
						+ EXCEPTION_LIST4PAGE);
			}
		}

		try {
			page = new Page<T>(startIndex, totalCount, pageSize, list);
		} catch (Exception e) {
			throw new DataAccessException(e, ibatisMapId + EXCEPTION_PAGE);
		}
		return page;
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#EXBySql(java.lang.Class,
	 *      java.util.Map)
	 */
	@SuppressWarnings("unchecked")
	public List<Map<String, Object>> ExecSql(String sql)
			throws DataAccessException {
		String mapping = IBATIS_EXECSQL;
		List<Map<String, Object>> list;
		try {
			list = this.getSqlMapClientTemplate().queryForList(mapping, sql);
		} catch (Exception e) {
			throw new DataAccessException(e, mapping + EXCEPTION_EXSQL);
		}
		return list;
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#count(java.lang.Class,
	 *      com.atjava.nebula.core.dao.ibatis.Condition)
	 */
	public <T extends BaseModel> Integer count(Class<T> entityClass,
			Condition cond) throws DataAccessException {
		Integer cnt = null;
		String countId = this.getDomain(entityClass) + IBATIS_COUNT;

		try {
			cnt = (Integer) this.getSqlMapClientTemplate().queryForObject(
					countId, cond);
		} catch (Exception e) {
			throw new DataAccessException(e, countId + EXCEPTION_COUNT);
		}
		return cnt;		
		//return count(entityClass, null,cond);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#count(com.atjava.nebula.core.model.BaseModel)
	 */
	public <T extends BaseModel> Integer count(T obj)
			throws DataAccessException {

		Condition cond = new Condition(obj);

		return count(obj.getClass(), cond);
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#count(java.lang.Class)
	 */
	public <T extends BaseModel> Integer count(Class<T> entityClass)
			throws DataAccessException {
		return count(entityClass, null);
	}
	/* 
	 * @see com.atjava.nebula.core.dao.BaseDao#count(java.lang.Class, com.atjava.nebula.core.dao.ibatis.projection.IProjection, com.atjava.nebula.core.dao.ibatis.Condition)
	 */
	public <T extends BaseModel> Integer count(Class<T> entityClass, IProjection projection, Condition cond) throws DataAccessException {
		Integer cnt = null;
		String countId = this.getDomain(entityClass) + IBATIS_COUNT_PROJ;
		ProjAndCond projCond = new ProjAndCond(projection, cond);
		try {
			cnt = (Integer) this.getSqlMapClientTemplate().queryForObject(
					countId, projCond);
		} catch (Exception e) {
			throw new DataAccessException(e, countId + EXCEPTION_COUNT);
		}
		return cnt;
	}

	/* 
	 * @see com.atjava.nebula.core.dao.BaseDao#count(com.atjava.nebula.core.model.BaseModel, com.atjava.nebula.core.dao.ibatis.projection.IProjection)
	 */
	public <T extends BaseModel> Integer count(T obj, IProjection projection) throws DataAccessException {
		Condition cond = new Condition(obj);
		return count(obj.getClass() ,projection,cond);
	}
	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#page(java.lang.Class,
	 *      com.atjava.nebula.core.dao.ibatis.projection1.IProjection,
	 *      com.atjava.nebula.core.dao.ibatis.Condition, int, int)
	 */
	@SuppressWarnings("unchecked")
	public <T extends BaseModel> Page<Map<String, Object>> page(
			Class<T> entityClass, IProjection projection, Condition cond,
			int pageNo, int pageSize) throws DataAccessException {
		Page<Map<String, Object>> page = null;
		List<Map<String, Object>> list = null;
		ProjAndCond projCond = new ProjAndCond(projection, cond);
		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		
		Integer totalCount = count(entityClass, projection, cond);
		String mapId = this.getDomain(entityClass) + IBATIS_GETBYPROJ;
		
		if ((totalCount == null) || (totalCount == 0)) {
			// throw new DataAccessException(ibatisMapId + EXCEPTION_COUNT);
			list = new ArrayList<Map<String, Object>>();
		} else {

			try {
				// getSqlMapClientTemplate().getSqlMapClient().
				if (pageSize > 0 && pageNo > 0) {
					list = getSqlMapClientTemplate().queryForList(mapId,
							projCond, (pageNo - 1) * pageSize, pageSize);
				} else {
					list = getSqlMapClientTemplate().queryForList(mapId,
							projCond);
				}
			} catch (Exception e) {
				throw new DataAccessException(e, mapId + EXCEPTION_LIST4PAGE);
			}
		}

		try {
			page = new Page<Map<String, Object>>(startIndex, totalCount,
					pageSize, list);
		} catch (Exception e) {
			throw new DataAccessException(e, mapId + EXCEPTION_PAGE);
		}
		return page;
	}

	/*
	 * @see com.atjava.nebula.core.dao.BaseDao#page(com.atjava.nebula.core.model.BaseModel,
	 *      com.atjava.nebula.core.dao.ibatis.projection1.IProjection, int, int)
	 */
	public <T extends BaseModel> Page<Map<String, Object>> page(T obj,
			IProjection projection, int pageNo, int pageSize)
			throws DataAccessException {
		Condition cond = new Condition(obj);
		return page(obj.getClass(), projection, cond, pageNo, pageSize);
	}

	/* 
	 * @see com.atjava.nebula.core.dao.BaseDao#ExecSql(java.lang.String, java.lang.Object[])
	 */
	public List<Map<String, Object>> ExecSql(
			String sql, Object... params) throws DataAccessException {
		String mapping = IBATIS_EXECSQL;
		List<Map<String, Object>> list;
		SqlScript script = new MsSQL2KScript();
		
		if (params.length == 1 && params[0] instanceof Object[]) {
			Object[] objs = (Object[]) params[0];
			sql = script.replaceParameter(sql, objs);
		} else {
			sql = script.replaceParameter(sql, params);
		}
		
		try {
			list = this.getSqlMapClientTemplate().queryForList(mapping, sql);
		} catch (Exception e) {
			throw new DataAccessException(e, mapping + EXCEPTION_EXSQL);
		}
		return list;
	}


}
